index.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. "use client";
  2. import { loginApi, registerApi, userInfoApi } from "@/api/login";
  3. import Box from "@/components/Box";
  4. import ButtonOwn from "@/components/ButtonOwn";
  5. import MobileField from "@/components/Fields/MobileField";
  6. import { useEventPoint } from "@/hooks/useEventPoint";
  7. import { Link, useRouter } from "@/i18n/routing";
  8. import { useSystemStore } from "@/stores/useSystemStore";
  9. import { useUserInfoStore } from "@/stores/useUserInfoStore";
  10. import { emailReg, neReg } from "@/utils";
  11. import { setCookies } from "@/utils/Cookies";
  12. import {
  13. Checkbox,
  14. DatePicker,
  15. DatePickerRef,
  16. Form,
  17. Input,
  18. Radio,
  19. Space,
  20. TextArea,
  21. Toast,
  22. } from "antd-mobile";
  23. import { PickerDate } from "antd-mobile/es/components/date-picker/util";
  24. import clsx from "clsx";
  25. import dayjs from "dayjs";
  26. import { useTranslations } from "next-intl";
  27. import { useSearchParams } from "next/navigation";
  28. import { FC, RefObject, useRef, useState } from "react";
  29. interface MobileFieldProps {
  30. value?: string;
  31. onChange?: (value: string) => void;
  32. }
  33. // const MobileField: FC<MobileFieldProps> = (props) => {
  34. // const t = useTranslations("form");
  35. // const { value, onChange } = props;
  36. //
  37. // const changeHandler = (value: string) => {
  38. // let newAmount = value.replace(/[^0-9]/g, "");
  39. // if (onChange) {
  40. // onChange(newAmount);
  41. // }
  42. // };
  43. // return (
  44. // <>
  45. // <Space align="center" className={"text-[#ccc]"}>
  46. // <Space align="center">+55</Space>
  47. // <Input
  48. // placeholder={t("phone")}
  49. // maxLength={11}
  50. // value={value}
  51. // onChange={(e) => changeHandler(e)}
  52. // />
  53. // </Space>
  54. // </>
  55. // );
  56. // };
  57. const SexMobile: FC<MobileFieldProps> = (props) => {
  58. const { value, onChange } = props;
  59. const handler = () => {
  60. console.log(`🚀🚀🚀🚀🚀-> in index.tsx on 58`, 123);
  61. };
  62. return (
  63. <div className={"w-[100%] border-4"} onClick={handler}>
  64. <Space align="center" className={"border-4 text-[#ccc]"}>
  65. <div className={"h-full w-[100%]"}>{value}</div>
  66. </Space>
  67. </div>
  68. );
  69. };
  70. interface FormProps {
  71. /**
  72. * 通讯地址
  73. */
  74. address?: string;
  75. /**
  76. * 用户头像
  77. */
  78. avatar_url?: string;
  79. /**
  80. * 生日:yyyy-MM-dd
  81. */
  82. birthday?: string;
  83. /**
  84. * 邮箱地址
  85. */
  86. email?: string;
  87. /**
  88. * 真实姓名
  89. */
  90. nick_name?: string;
  91. /**
  92. * 外部ID
  93. */
  94. open_id?: string;
  95. /**
  96. * 身份护照
  97. */
  98. passport?: string;
  99. /**
  100. * 用户密码
  101. */
  102. pwd: string;
  103. /**
  104. * 推荐码
  105. */
  106. referrer_code?: string;
  107. /**
  108. * 用户名
  109. */
  110. user_name?: string;
  111. /**
  112. * 用户电话号码
  113. */
  114. user_phone: string;
  115. /**
  116. * 用户类型
  117. */
  118. user_type?: string;
  119. sex?: number;
  120. /**
  121. * 渠道链接
  122. */
  123. channel_code?: string;
  124. code_phone?: string;
  125. }
  126. interface FormInitStateTypes extends FormProps {
  127. mobile?: {
  128. preValue: string;
  129. realValue: string;
  130. };
  131. }
  132. type FormType = "login" | "register";
  133. interface Props {
  134. type?: FormType;
  135. }
  136. const FormComponent: FC<Props> = (props) => {
  137. const { type = "register" } = props;
  138. const isStrictMode = useSystemStore().identity_verify.register === 1;
  139. const { setUserInfo } = useUserInfoStore();
  140. const t = useTranslations();
  141. const searchParams = useSearchParams();
  142. const [statusText, setStatusText] = useState("");
  143. const { eventLogin, eventRegister } = useEventPoint();
  144. /// 密码可见
  145. const [visible, setVisible] = useState(false);
  146. const spanClassName = clsx("iconfont", {
  147. "icon-kejian": visible,
  148. "icon-bukejian": !visible,
  149. });
  150. // 是否同意协议
  151. const [checkBoxValue, setCheckBoxValue] = useState<boolean>(false);
  152. const router = useRouter();
  153. /// 初始值
  154. const params = useRef<FormInitStateTypes>({
  155. user_phone: "",
  156. mobile: { preValue: "55", realValue: "" },
  157. pwd: "",
  158. sex: 0,
  159. address: undefined,
  160. birthday: undefined,
  161. email: undefined,
  162. passport: undefined,
  163. referrer_code: undefined,
  164. });
  165. const onFinish = (values: FormInitStateTypes) => {
  166. const { mobile } = values;
  167. const newValue = {
  168. ...values,
  169. user_phone: `${mobile?.preValue}${mobile?.realValue}`,
  170. code_phone: mobile?.preValue,
  171. };
  172. delete newValue.mobile;
  173. if (isStrictMode) {
  174. strictHandler(newValue as FormProps);
  175. } else {
  176. looseHandler(newValue as FormProps);
  177. }
  178. };
  179. /// 严格模式
  180. const strictHandler = (values: FormProps) => {
  181. if (type !== "login") {
  182. if (!checkBoxValue) {
  183. Toast.show({
  184. content: t("form.readyAgreement"),
  185. });
  186. return;
  187. }
  188. values.birthday = dayjs(values.birthday).format("YYYY/MM/DD");
  189. }
  190. looseHandler(values);
  191. };
  192. const loginHandler = async (values: FormProps) => {
  193. return new Promise(async (resolve, reject) => {
  194. const loginResult = await loginApi(values).catch((error) => {
  195. let text = error ? t(`code.${error.data.code}`) : t(`code.${500}`);
  196. Toast.show({
  197. content: text,
  198. });
  199. });
  200. if (loginResult?.code === 200) {
  201. setCookies("Token", loginResult.data.token as string);
  202. const result = await userInfoApi();
  203. if (result.code === 200) {
  204. setUserInfo(result.data);
  205. resolve(result);
  206. return result;
  207. }
  208. } else {
  209. reject();
  210. }
  211. });
  212. };
  213. /// 宽松模式
  214. const looseHandler = async (values: FormProps) => {
  215. // 请求
  216. Toast.show({
  217. icon: "loading",
  218. duration: 0,
  219. });
  220. // 注册
  221. if (type === "register") {
  222. const newValues = {
  223. ...values,
  224. referrer_code: sessionStorage.getItem("shareId") ?? undefined,
  225. channel_code: localStorage.getItem("channel_code") ?? undefined,
  226. // 轮盘邀请
  227. turntable_id: Number(sessionStorage.getItem("turntable_id")) ?? undefined,
  228. turntable_user_id: Number(sessionStorage.getItem("turntable_user_id")) ?? undefined,
  229. turntable_time: Number(sessionStorage.getItem("turntable_time")) ?? undefined,
  230. };
  231. registerApi(newValues)
  232. .then(async (res) => {
  233. if (res.code === 200) {
  234. loginHandler({
  235. pwd: values.pwd,
  236. user_phone: values.user_phone,
  237. code_phone: values.code_phone,
  238. }).then(() => {
  239. router.replace("/recharge");
  240. Toast.clear();
  241. });
  242. eventRegister();
  243. }
  244. })
  245. .catch((error) => {
  246. console.log(`🚀🚀🚀🚀🚀-> in index.tsx on 257`, error);
  247. if (error.data?.code === 1017) {
  248. sessionStorage.removeItem("shareId");
  249. }
  250. Toast.show({
  251. content: t(`code.${error.data?.code ?? 500}`),
  252. });
  253. });
  254. } else {
  255. /// 登录
  256. loginHandler(values).then(() => {
  257. Toast.clear();
  258. const redirect = searchParams.get("redirect")
  259. ? `/${searchParams.get("redirect")}`
  260. : "/";
  261. router.replace(redirect);
  262. eventLogin();
  263. });
  264. }
  265. };
  266. const onConfirm = (value: PickerDate) => {
  267. const isChildren = dayjs().subtract(18, "year").isBefore(value);
  268. if (isChildren) {
  269. Toast.show({
  270. icon: "fail",
  271. content: t("form.NotSuitableForChildren"),
  272. });
  273. }
  274. };
  275. const ageValidator = (rule: any, value: PickerDate) => {
  276. const isChildren = dayjs().subtract(18, "year").isBefore(value);
  277. if (isChildren) {
  278. return Promise.reject(new Error(rule.message));
  279. } else {
  280. return Promise.resolve();
  281. }
  282. };
  283. // 手机号验证规则
  284. const checkMobile = (_: any, value: any) => {
  285. if (value.realValue.length < 10) {
  286. return Promise.reject(new Error(t("form.phoneMinReg")));
  287. } else {
  288. return Promise.resolve();
  289. }
  290. };
  291. return (
  292. <Box className={"custom-form"}>
  293. <Form
  294. style={{
  295. "--border-bottom": "none",
  296. "--border-top": "none",
  297. "--border-inner": "none",
  298. }}
  299. initialValues={params.current}
  300. onFinish={onFinish}
  301. footer={
  302. <ButtonOwn active>
  303. {type === "login" ? t("form.loginText") : t("form.registerText")}
  304. </ButtonOwn>
  305. }
  306. >
  307. <Form.Item name="mobile" rules={[{ required: true, validator: checkMobile }]}>
  308. <MobileField />
  309. </Form.Item>
  310. <Form.Item
  311. name="pwd"
  312. label=""
  313. extra={
  314. <span className={spanClassName} onClick={() => setVisible(!visible)}></span>
  315. }
  316. rules={[
  317. { required: true, message: t("form.passwordReg") },
  318. { min: 6, max: 20, message: t("form.passwordMinReg") },
  319. ]}
  320. >
  321. <Input
  322. placeholder={t("form.password")}
  323. maxLength={20}
  324. type={visible ? "text" : "password"}
  325. />
  326. </Form.Item>
  327. {type !== "login" && isStrictMode ? (
  328. <>
  329. <Form.Item
  330. name="user_name"
  331. label=""
  332. rules={[{ required: true, message: t("form.usernameReg") }]}
  333. >
  334. <Input placeholder={t("form.username")} />
  335. </Form.Item>
  336. <Form.Item
  337. name="birthday"
  338. clickable={false}
  339. trigger={"onConfirm"}
  340. arrowIcon={<i className={"iconfont icon-xiangyou1"}></i>}
  341. onClick={(e, datePickerRef: RefObject<DatePickerRef>) => {
  342. datePickerRef.current?.open();
  343. }}
  344. rules={[
  345. { required: true, message: t("form.birthdayReg") },
  346. {
  347. message: t("form.NotSuitableForChildren"),
  348. validator: ageValidator,
  349. },
  350. ]}
  351. >
  352. <DatePicker
  353. getContainer={null}
  354. min={dayjs().subtract(50, "year").toDate()}
  355. max={dayjs().toDate()}
  356. style={{ background: "#fff", color: "#000" }}
  357. >
  358. {(value) =>
  359. value ? (
  360. dayjs(value).format("YYYY/MM/DD")
  361. ) : (
  362. <span className={"text-[#ccc]"}>{t("form.birthday")}</span>
  363. )
  364. }
  365. </DatePicker>
  366. </Form.Item>
  367. <Form.Item
  368. name="email"
  369. label=""
  370. rules={[
  371. { required: true, message: t("form.emailReg"), pattern: emailReg },
  372. ]}
  373. >
  374. <Input placeholder={t("form.email")} />
  375. </Form.Item>
  376. <Form.Item name="sex">
  377. <Radio.Group>
  378. <Radio
  379. value={0}
  380. className={"mr-[0.1rem]"}
  381. style={{ "--icon-size": "18px" }}
  382. >
  383. {t("form.sexMan")}
  384. </Radio>
  385. <Radio value={1} style={{ "--icon-size": "18px" }}>
  386. {t("form.sexWoman")}
  387. </Radio>
  388. </Radio.Group>
  389. </Form.Item>
  390. <Form.Item
  391. name="passport"
  392. label=""
  393. rules={[{ required: true, message: t("form.cardReg"), pattern: neReg }]}
  394. >
  395. <Input placeholder={t("form.card")} maxLength={11} type={"text"} />
  396. </Form.Item>
  397. <Form.Item
  398. name="address"
  399. label=""
  400. rules={[{ required: true, message: t("form.addressReg") }]}
  401. >
  402. <TextArea placeholder={t("form.address")} maxLength={40} />
  403. </Form.Item>
  404. <div className={"flex px-[0.1rem]"}>
  405. <Checkbox
  406. block
  407. style={{ "--icon-size": "16px" }}
  408. checked={checkBoxValue}
  409. onChange={(value) => setCheckBoxValue(value)}
  410. ></Checkbox>
  411. <div className={"ml-[10px] select-none break-all text-[12px]"}>
  412. {t("form.agreement")}
  413. <Link href={"/preventLaunderMoney"} className={"text-[#1677ff]"}>
  414. {t("form.moneyAgreement")}
  415. </Link>
  416. <Link href={"/terms"} className={"text-[#1677ff]"}>
  417. {t("form.serverAgreement")}
  418. </Link>
  419. {t("form.agreementAnd")}
  420. <Link href={"/gamingPolicy"} className={"text-[#1677ff]"}>
  421. {t("form.childrenAgreement")}
  422. </Link>
  423. </div>
  424. </div>
  425. </>
  426. ) : null}
  427. </Form>
  428. <div className="mb-[0.2rem] flex justify-between text-[0.12rem]">
  429. {type == "login" ? (
  430. <>
  431. <Link className={"text-[#fff]"} href="/resetPhone">
  432. {t("LoginPage.forgetPwd")}
  433. </Link>
  434. <Link className={"text-[#fff]"} href="/register">
  435. {t("LoginPage.registerGo")}
  436. </Link>
  437. </>
  438. ) : (
  439. <Link className={"w-[100%] text-center text-[#fff]"} href="/login" replace>
  440. {t("LoginPage.loginGo")}
  441. </Link>
  442. )}
  443. </div>
  444. </Box>
  445. );
  446. };
  447. export default FormComponent;